home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 090 / me39src2.arc / LINE.C < prev    next >
Encoding:
C/C++ Source or Header  |  1987-07-16  |  15.1 KB  |  586 lines

  1. /*
  2.  * The functions in this file are a general set of line management utilities.
  3.  * They are the only routines that touch the text. They also touch the buffer
  4.  * and window structures, to make sure that the necessary updating gets done.
  5.  * There are routines in this file that handle the kill buffer too. It isn't
  6.  * here for any good reason.
  7.  *
  8.  * Note that this code only updates the dot and mark values in the window list.
  9.  * Since all the code acts on the current window, the buffer that we are
  10.  * editing must be being displayed, which means that "b_nwnd" is non zero,
  11.  * which means that the dot and mark values in the buffer headers are nonsense.
  12.  */
  13.  
  14. #include    <stdio.h>
  15. #include    "estruct.h"
  16. #include    "edef.h"
  17.  
  18. KILL *ykbuf;    /* ptr to current kill buffer chunk being yanked */
  19. int ykboff;    /* offset into that chunk */
  20.  
  21. /*
  22.  * This routine allocates a block of memory large enough to hold a LINE
  23.  * containing "used" characters. The block is always rounded up a bit. Return
  24.  * a pointer to the new block, or NULL if there isn't any memory left. Print a
  25.  * message in the message line if no space.
  26.  */
  27. LINE *lalloc(used)
  28.  
  29. register int    used;
  30.  
  31. {
  32.     register LINE    *lp;
  33.     register int    size;
  34.     char *malloc();
  35.  
  36.     size = (used+NBLOCK-1) & ~(NBLOCK-1);
  37.     if (size == 0)                /* Assume that an empty */
  38.         size = NBLOCK;            /* line is for type-in. */
  39.     if ((lp = (LINE *) malloc(sizeof(LINE)+size)) == NULL) {
  40.         mlwrite("[OUT OF MEMORY]");
  41.         return (NULL);
  42.     }
  43.     lp->l_size = size;
  44.     lp->l_used = used;
  45.     return (lp);
  46. }
  47.  
  48. /*
  49.  * Delete line "lp". Fix all of the links that might point at it (they are
  50.  * moved to offset 0 of the next line. Unlink the line from whatever buffer it
  51.  * might be in. Release the memory. The buffers are updated too; the magic
  52.  * conditions described in the above comments don't hold here.
  53.  */
  54. lfree(lp)
  55. register LINE    *lp;
  56. {
  57.     register BUFFER *bp;
  58.     register WINDOW *wp;
  59.  
  60.     wp = wheadp;
  61.     while (wp != NULL) {
  62.         if (wp->w_linep == lp)
  63.             wp->w_linep = lp->l_fp;
  64.         if (wp->w_dotp    == lp) {
  65.             wp->w_dotp  = lp->l_fp;
  66.             wp->w_doto  = 0;
  67.         }
  68.         if (wp->w_markp == lp) {
  69.             wp->w_markp = lp->l_fp;
  70.             wp->w_marko = 0;
  71.         }
  72.         wp = wp->w_wndp;
  73.     }
  74.     bp = bheadp;
  75.     while (bp != NULL) {
  76.         if (bp->b_nwnd == 0) {
  77.             if (bp->b_dotp    == lp) {
  78.                 bp->b_dotp = lp->l_fp;
  79.                 bp->b_doto = 0;
  80.             }
  81.             if (bp->b_markp == lp) {
  82.                 bp->b_markp = lp->l_fp;
  83.                 bp->b_marko = 0;
  84.             }
  85.         }
  86.         bp = bp->b_bufp;
  87.     }
  88.     lp->l_bp->l_fp = lp->l_fp;
  89.     lp->l_fp->l_bp = lp->l_bp;
  90.     free((char *) lp);
  91. }
  92.  
  93. /*
  94.  * This routine gets called when a character is changed in place in the current
  95.  * buffer. It updates all of the required flags in the buffer and window
  96.  * system. The flag used is passed as an argument; if the buffer is being
  97.  * displayed in more than 1 window we change EDIT t HARD. Set MODE if the
  98.  * mode line needs to be updated (the "*" has to be set).
  99.  */
  100. lchange(flag)
  101. register int    flag;
  102. {
  103.     register WINDOW *wp;
  104.  
  105.     if (curbp->b_nwnd != 1)         /* Ensure hard.     */
  106.         flag = WFHARD;
  107.     if ((curbp->b_flag&BFCHG) == 0) {    /* First change, so    */
  108.         flag |= WFMODE;         /* update mode lines.    */
  109.         curbp->b_flag |= BFCHG;
  110.     }
  111.     wp = wheadp;
  112.     while (wp != NULL) {
  113.         if (wp->w_bufp == curbp)
  114.             wp->w_flag |= flag;
  115.         wp = wp->w_wndp;
  116.     }
  117. }
  118.  
  119. insspace(f, n)    /* insert spaces forward into text */
  120.  
  121. int f, n;    /* default flag and numeric argument */
  122.  
  123. {
  124.     linsert(n, ' ');
  125.     backchar(f, n);
  126. }
  127.  
  128. /*
  129.  * Insert "n" copies of the character "c" at the current location of dot. In
  130.  * the easy case all that happens is the text is stored in the line. In the
  131.  * hard case, the line has to be reallocated. When the window list is updated,
  132.  * take special care; I screwed it up once. You always update dot in the
  133.  * current window. You update mark, and a dot in another window, if it is
  134.  * greater than the place where you did the insert. Return TRUE if all is
  135.  * well, and FALSE on errors.
  136.  */
  137. linsert(n, c)
  138. {
  139.     register char    *cp1;
  140.     register char    *cp2;
  141.     register LINE    *lp1;
  142.     register LINE    *lp2;
  143.     register LINE    *lp3;
  144.     register int    doto;
  145.     register int    i;
  146.     register WINDOW *wp;
  147.  
  148.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  149.         return(rdonly());    /* we are in read only mode    */
  150.     lchange(WFEDIT);
  151.     lp1 = curwp->w_dotp;            /* Current line     */
  152.     if (lp1 == curbp->b_linep) {        /* At the end: special    */
  153.         if (curwp->w_doto != 0) {
  154.             mlwrite("bug: linsert");
  155.             return (FALSE);
  156.         }
  157.         if ((lp2=lalloc(n)) == NULL)    /* Allocate new line    */
  158.             return (FALSE);
  159.         lp3 = lp1->l_bp;        /* Previous line    */
  160.         lp3->l_fp = lp2;        /* Link in        */
  161.         lp2->l_fp = lp1;
  162.         lp1->l_bp = lp2;
  163.         lp2->l_bp = lp3;
  164.         for (i=0; i<n; ++i)
  165.             lp2->l_text[i] = c;
  166.         curwp->w_dotp = lp2;
  167.         curwp->w_doto = n;
  168.         return (TRUE);
  169.     }
  170.     doto = curwp->w_doto;            /* Save for later.    */
  171.     if (lp1->l_used+n > lp1->l_size) {    /* Hard: reallocate    */
  172.         if ((lp2=lalloc(lp1->l_used+n)) == NULL)
  173.             return (FALSE);
  174.         cp1 = &lp1->l_text[0];
  175.         cp2 = &lp2->l_text[0];
  176.         while (cp1 != &lp1->l_text[doto])
  177.             *cp2++ = *cp1++;
  178.         cp2 += n;
  179.         while (cp1 != &lp1->l_text[lp1->l_used])
  180.             *cp2++ = *cp1++;
  181.         lp1->l_bp->l_fp = lp2;
  182.         lp2->l_fp = lp1->l_fp;
  183.         lp1->l_fp->l_bp = lp2;
  184.         lp2->l_bp = lp1->l_bp;
  185.         free((char *) lp1);
  186.     } else {                /* Easy: in place    */
  187.         lp2 = lp1;            /* Pretend new line    */
  188.         lp2->l_used += n;
  189.         cp2 = &lp1->l_text[lp1->l_used];
  190.         cp1 = cp2-n;
  191.         while (cp1 != &lp1->l_text[doto])
  192.             *--cp2 = *--cp1;
  193.     }
  194.     for (i=0; i<n; ++i)            /* Add the characters    */
  195.         lp2->l_text[doto+i] = c;
  196.     wp = wheadp;                /* Update windows    */
  197.     while (wp != NULL) {
  198.         if (wp->w_linep == lp1)
  199.             wp->w_linep = lp2;
  200.         if (wp->w_dotp == lp1) {
  201.             wp->w_dotp = lp2;
  202.             if (wp==curwp || wp->w_doto>doto)
  203.                 wp->w_doto += n;
  204.         }
  205.         if (wp->w_markp == lp1) {
  206.             wp->w_markp = lp2;
  207.             if (wp->w_marko > doto)
  208.                 wp->w_marko += n;
  209.         }
  210.         wp = wp->w_wndp;
  211.     }
  212.     return (TRUE);
  213. }
  214.  
  215. /*
  216.  * Insert a newline into the buffer at the current location of dot in the
  217.  * current window. The funny ass-backwards way it does things is not a botch;
  218.  * it just makes the last line in the file not a special case. Return TRUE if
  219.  * everything works out and FALSE on error (memory allocation failure). The
  220.  * update of dot and mark is a bit easier then in the above case, because the
  221.  * split forces more updating.
  222.  */
  223. lnewline()
  224. {
  225.     register char    *cp1;
  226.     register char    *cp2;
  227.     register LINE    *lp1;
  228.     register LINE    *lp2;
  229.     register int    doto;
  230.     register WINDOW *wp;
  231.  
  232.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  233.         return(rdonly());    /* we are in read only mode    */
  234.     lchange(WFHARD);
  235.     lp1  = curwp->w_dotp;            /* Get the address and    */
  236.     doto = curwp->w_doto;            /* offset of "."    */
  237.     if ((lp2=lalloc(doto)) == NULL)     /* New first half line    */
  238.         return (FALSE);
  239.     cp1 = &lp1->l_text[0];            /* Shuffle text around    */
  240.     cp2 = &lp2->l_text[0];
  241.     while (cp1 != &lp1->l_text[doto])
  242.         *cp2++ = *cp1++;
  243.     cp2 = &lp1->l_text[0];
  244.     while (cp1 != &lp1->l_text[lp1->l_used])
  245.         *cp2++ = *cp1++;
  246.     lp1->l_used -= doto;
  247.     lp2->l_bp = lp1->l_bp;
  248.     lp1->l_bp = lp2;
  249.     lp2->l_bp->l_fp = lp2;
  250.     lp2->l_fp = lp1;
  251.     wp = wheadp;                /* Windows        */
  252.     while (wp != NULL) {
  253.         if (wp->w_linep == lp1)
  254.             wp->w_linep = lp2;
  255.         if (wp->w_dotp == lp1) {
  256.             if (wp->w_doto < doto)
  257.                 wp->w_dotp = lp2;
  258.             else
  259.                 wp->w_doto -= doto;
  260.         }
  261.         if (wp->w_markp == lp1) {
  262.             if (wp->w_marko < doto)
  263.                 wp->w_markp = lp2;
  264.             else
  265.                 wp->w_marko -= doto;
  266.         }
  267.         wp = wp->w_wndp;
  268.     }
  269.     return (TRUE);
  270. }
  271.  
  272. /*
  273.  * This function deletes "n" bytes, starting at dot. It understands how do deal
  274.  * with end of lines, etc. It returns TRUE if all of the characters were
  275.  * deleted, and FALSE if they were not (because dot ran into the end of the
  276.  * buffer. The "kflag" is TRUE if the text should be put in the kill buffer.
  277.  */
  278. ldelete(n, kflag)
  279.  
  280. long n;     /* # of chars to delete */
  281. int kflag;    /* put killed text in kill buffer flag */
  282.  
  283. {
  284.     register char    *cp1;
  285.     register char    *cp2;
  286.     register LINE    *dotp;
  287.     register int    doto;
  288.     register int    chunk;
  289.     register WINDOW *wp;
  290.  
  291.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  292.         return(rdonly());    /* we are in read only mode    */
  293.     while (n != 0) {
  294.         dotp = curwp->w_dotp;
  295.         doto = curwp->w_doto;
  296.         if (dotp == curbp->b_linep)    /* Hit end of buffer.    */
  297.             return (FALSE);
  298.         chunk = dotp->l_used-doto;    /* Size of chunk.    */
  299.         if (chunk > n)
  300.             chunk = n;
  301.         if (chunk == 0) {        /* End of line, merge.    */
  302.             lchange(WFHARD);
  303.             if (ldelnewline() == FALSE
  304.             || (kflag!=FALSE && kinsert('\n')==FALSE))
  305.                 return (FALSE);
  306.             --n;
  307.             continue;
  308.         }
  309.         lchange(WFEDIT);
  310.         cp1 = &dotp->l_text[doto];    /* Scrunch text.    */
  311.         cp2 = cp1 + chunk;
  312.         if (kflag != FALSE) {        /* Kill?        */
  313.             while (cp1 != cp2) {
  314.                 if (kinsert(*cp1) == FALSE)
  315.                     return (FALSE);
  316.                 ++cp1;
  317.             }
  318.             cp1 = &dotp->l_text[doto];
  319.         }
  320.         while (cp2 != &dotp->l_text[dotp->l_used])
  321.             *cp1++ = *cp2++;
  322.         dotp->l_used -= chunk;
  323.         wp = wheadp;            /* Fix windows        */
  324.         while (wp != NULL) {
  325.             if (wp->w_dotp==dotp && wp->w_doto>=doto) {
  326.                 wp->w_doto -= chunk;
  327.                 if (wp->w_doto < doto)
  328.                     wp->w_doto = doto;
  329.             }
  330.             if (wp->w_markp==dotp && wp->w_marko>=doto) {
  331.                 wp->w_marko -= chunk;
  332.                 if (wp->w_marko < doto)
  333.                     wp->w_marko = doto;
  334.             }
  335.             wp = wp->w_wndp;
  336.         }
  337.         n -= chunk;
  338.     }
  339.     return (TRUE);
  340. }
  341.  
  342. /* getctext:    grab and return a string with the text of
  343.         the current line
  344. */
  345.  
  346. char *getctext()
  347.  
  348. {
  349.     register LINE *lp;    /* line to copy */
  350.     register int size;    /* length of line to return */
  351.     register char *sp;    /* string pointer into line */
  352.     register char *dp;    /* string pointer into returned line */
  353.     char rline[NSTRING];    /* line to return */
  354.  
  355.     /* find the contents of the current line and its length */
  356.     lp = curwp->w_dotp;
  357.     sp = lp->l_text;
  358.     size = lp->l_used;
  359.     if (size >= NSTRING)
  360.         size = NSTRING - 1;
  361.  
  362.     /* copy it across */
  363.     dp = rline;
  364.     while (size--)
  365.         *dp++ = *sp++;
  366.     *dp = 0;
  367.     return(rline);
  368. }
  369.  
  370. /* putctext:    replace the current line with the passed in text    */
  371.  
  372. putctext(iline)
  373.  
  374. char *iline;    /* contents of new line */
  375.  
  376. {
  377.     register int status;
  378.  
  379.     /* delete the current line */
  380.     curwp->w_doto = 0;    /* starting at the beginning of the line */
  381.     if ((status = killtext(TRUE, 1)) != TRUE)
  382.         return(status);
  383.  
  384.     /* insert the new line */
  385.     while (*iline) {
  386.         if (*iline == '\n') {
  387.             if (lnewline() != TRUE)
  388.                 return(FALSE);
  389.         } else {
  390.             if (linsert(1, *iline) != TRUE)
  391.                 return(FALSE);
  392.         }
  393.         ++iline;
  394.     }
  395.     status = lnewline();
  396.     backline(TRUE, 1);
  397.     return(status);
  398. }
  399.  
  400. /*
  401.  * Delete a newline. Join the current line with the next line. If the next line
  402.  * is the magic header line always return TRUE; merging the last line with the
  403.  * header line can be thought of as always being a successful operation, even
  404.  * if nothing is done, and this makes the kill buffer work "right". Easy cases
  405.  * can be done by shuffling data around. Hard cases require that lines be moved
  406.  * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
  407.  * "ldelete" only.
  408.  */
  409. ldelnewline()
  410. {
  411.     register char    *cp1;
  412.     register char    *cp2;
  413.     register LINE    *lp1;
  414.     register LINE    *lp2;
  415.     register LINE    *lp3;
  416.     register WINDOW *wp;
  417.  
  418.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  419.         return(rdonly());    /* we are in read only mode    */
  420.     lp1 = curwp->w_dotp;
  421.     lp2 = lp1->l_fp;
  422.     if (lp2 == curbp->b_linep) {        /* At the buffer end.    */
  423.         if (lp1->l_used == 0)        /* Blank line.        */
  424.             lfree(lp1);
  425.         return (TRUE);
  426.     }
  427.     if (lp2->l_used <= lp1->l_size-lp1->l_used) {
  428.         cp1 = &lp1->l_text[lp1->l_used];
  429.         cp2 = &lp2->l_text[0];
  430.         while (cp2 != &lp2->l_text[lp2->l_used])
  431.             *cp1++ = *cp2++;
  432.         wp = wheadp;
  433.         while (wp != NULL) {
  434.             if (wp->w_linep == lp2)
  435.                 wp->w_linep = lp1;
  436.             if (wp->w_dotp == lp2) {
  437.                 wp->w_dotp  = lp1;
  438.                 wp->w_doto += lp1->l_used;
  439.             }
  440.             if (wp->w_markp == lp2) {
  441.                 wp->w_markp  = lp1;
  442.                 wp->w_marko += lp1->l_used;
  443.             }
  444.             wp = wp->w_wndp;
  445.         }
  446.         lp1->l_used += lp2->l_used;
  447.         lp1->l_fp = lp2->l_fp;
  448.         lp2->l_fp->l_bp = lp1;
  449.         free((char *) lp2);
  450.         return (TRUE);
  451.     }
  452.     if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
  453.         return (FALSE);
  454.     cp1 = &lp1->l_text[0];
  455.     cp2 = &lp3->l_text[0];
  456.     while (cp1 != &lp1->l_text[lp1->l_used])
  457.         *cp2++ = *cp1++;
  458.     cp1 = &lp2->l_text[0];
  459.     while (cp1 != &lp2->l_text[lp2->l_used])
  460.         *cp2++ = *cp1++;
  461.     lp1->l_bp->l_fp = lp3;
  462.     lp3->l_fp = lp2->l_fp;
  463.     lp2->l_fp->l_bp = lp3;
  464.     lp3->l_bp = lp1->l_bp;
  465.     wp = wheadp;
  466.     while (wp != NULL) {
  467.         if (wp->w_linep==lp1 || wp->w_linep==lp2)
  468.             wp->w_linep = lp3;
  469.         if (wp->w_dotp == lp1)
  470.             wp->w_dotp  = lp3;
  471.         else if (wp->w_dotp == lp2) {
  472.             wp->w_dotp  = lp3;
  473.             wp->w_doto += lp1->l_used;
  474.         }
  475.         if (wp->w_markp == lp1)
  476.             wp->w_markp  = lp3;
  477.         else if (wp->w_markp == lp2) {
  478.             wp->w_markp  = lp3;
  479.             wp->w_marko += lp1->l_used;
  480.         }
  481.         wp = wp->w_wndp;
  482.     }
  483.     free((char *) lp1);
  484.     free((char *) lp2);
  485.     return (TRUE);
  486. }
  487.  
  488. /*
  489.  * Delete all of the text saved in the kill buffer. Called by commands when a
  490.  * new kill context is being created. The kill buffer array is released, just
  491.  * in case the buffer has grown to immense size. No errors.
  492.  */
  493. kdelete()
  494. {
  495.     KILL *kp;    /* ptr to scan kill buffer chunk list */
  496.  
  497.     if (kbufh != NULL) {
  498.  
  499.         /* first, delete all the chunks */
  500.         kbufp = kbufh;
  501.         while (kbufp != NULL) {
  502.             kp = kbufp->d_next;
  503.             free(kbufp);
  504.             kbufp = kp;
  505.         }
  506.  
  507.         /* and reset all the kill buffer pointers */
  508.         kbufh = kbufp = NULL;
  509.         kused = KBLOCK;             
  510.     }
  511. }
  512.  
  513. /*
  514.  * Insert a character to the kill buffer, allocating new chunks as needed.
  515.  * Return TRUE if all is well, and FALSE on errors.
  516.  */
  517.  
  518. kinsert(c)
  519.  
  520. int c;        /* character to insert in the kill buffer */
  521.  
  522. {
  523.     KILL *nchunk;    /* ptr to newly malloced chunk */
  524.  
  525.     /* check to see if we need a new chunk */
  526.     if (kused >= KBLOCK) {
  527.         if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULL)
  528.             return(FALSE);
  529.         if (kbufh == NULL)    /* set head ptr if first time */
  530.             kbufh = nchunk;
  531.         if (kbufp != NULL)    /* point the current to this new one */
  532.             kbufp->d_next = nchunk;
  533.         kbufp = nchunk;
  534.         kbufp->d_next = NULL;
  535.         kused = 0;
  536.     }
  537.  
  538.     /* and now insert the character */
  539.     kbufp->d_chunk[kused++] = c;
  540.     return(TRUE);
  541. }
  542.  
  543. /*
  544.  * Yank text back from the kill buffer. This is really easy. All of the work
  545.  * is done by the standard insert routines. All you do is run the loop, and
  546.  * check for errors. Bound to "C-Y".
  547.  */
  548. yank(f, n)
  549. {
  550.     register int    c;
  551.     register int    i;
  552.     register char    *sp;    /* pointer into string to insert */
  553.     KILL *kp;        /* pointer into kill buffer */
  554.  
  555.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  556.         return(rdonly());    /* we are in read only mode    */
  557.     if (n < 0)
  558.         return (FALSE);
  559.     /* make sure there is something to yank */
  560.     if (kbufh == NULL)
  561.         return(TRUE);        /* not an error, just nothing */
  562.  
  563.     /* for each time.... */
  564.     while (n--) {
  565.         kp = kbufh;
  566.         while (kp != NULL) {
  567.             if (kp->d_next == NULL)
  568.                 i = kused;
  569.             else
  570.                 i = KBLOCK;
  571.             sp = kp->d_chunk;
  572.             while (i--) {
  573.                 if ((c = *sp++) == '\n') {
  574.                     if (lnewline() == FALSE)
  575.                         return (FALSE);
  576.                 } else {
  577.                     if (linsert(1, c) == FALSE)
  578.                         return (FALSE);
  579.                 }
  580.             }
  581.             kp = kp->d_next;
  582.         }
  583.     }
  584.     return (TRUE);
  585. }
  586.